# coding:utf-8
'''
 the simple server for 4xDevice
 2018-11-06
'''
'''
    print('{0:{1}^9}\t'.format(ii,chr(12288)),end = '') # 居中对齐
    print('{0:{1}<9}\t'.format(ii,chr(12288)),end = '') # 左对齐  
    chr(12288)  中文空格
'''

import os, sys
import socket, select
import queue
import time
import re, struct
import logging
import binascii

class MyLog(object):
    def __init__(self, level = logging.DEBUG,
                        name = "4xServer",
                        log_name = time.strftime("%Y-%m-%d-%H.log", time.localtime()),
                        log_path = os.path.dirname(os.path.abspath(__file__))):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(level)
        if not os.path.isdir(log_path):
            os.mkdir(log_path)
        logfile_path = os.path.join(log_path, log_name)
        log_handler = logging.FileHandler(logfile_path)
        log_handler.setFormatter(logging.Formatter("%(message)s"))
        self.logger.addHandler(log_handler)

        console_handler = logging.StreamHandler()
        #console_handler.setFormatter(logging.Formatter("[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s]:%(message)s"))
        console_handler.setFormatter(logging.Formatter("%(message)s"))
        self.logger.addHandler(console_handler)
        self.blackstr = ' '*20

    def get_time_stamp(self):
        ct = time.time()
        local_time = time.localtime(ct)
        data_head = time.strftime("[%m-%d %H:%M:%S", local_time)
        data_secs = (ct - int(ct)) * 1000
        time_stamp = "{0}.{1:0>3}]".format(data_head, int(data_secs))
        return time_stamp

    def info(self, msg, *args, **kwargs):
        self.logger.info(msg, *args, **kwargs)

    def debug(self, msg, *args, **kwargs):
        self.logger.debug(msg, *args, **kwargs)

    def log_tick(self, msg):
        self.logger.info("{0}:{1}".format(self.get_time_stamp(), msg))

    def log(self, msg):
        self.logger.info("{0}:{1}".format(self.blackstr, msg))

gLog = MyLog()

class MyBasePool(list):
    '''All of the connected socket array'''
    def __init__(self, *args):
        list.__init__(self, *args)

class TcpServer(object):
    def __init__(self, addr = 'localhost', port = 9004):
        self.server = []
        #check ip addr
        if isinstance(addr, str):
            if addr != 'localhost':
                a = re.match(r'\d+\.\d+\.\d+\.\d+', addr)
                if a.group() != addr:
                    raise ValueError("invalid addr")
        else:
            raise TypeError("type err invalid addr")
        self.addr = str(addr)
        #check port
        if isinstance(port, int):
            if port < 1024 or port > 65535:
                raise ValueError("invalid port")
        else:
            raise TypeError("type err invalid port")
        self.port = int(port)
        self.ipaddr = (self.addr, self.port)
        #gLog.info("inited addr is {0}".format(self.ipaddr))
        self.ev_str = {0:u"求救恢复", 1:u"求救", 3:u"故障", 10:u"恢复正常", 14:u"浓度报警", 15:u"异常报警"}

    def simple_run(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(self.ipaddr)
        self.socket.listen(5)
        client, addr = self.socket.accept()
        while 1:
            data = client.recv(1024)
            if len(data) == 0:
                client.close()
                break
            else:
                gLog.log_tick("recv data from {0}".format(client.getpeername()))
                self.unpack(data)

    def select_run(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(self.ipaddr)
        self.socket.listen(1)
        self.server.append(self.socket)

        while 1:
            try:
                rs, ws, es = select.select(self.server, [], [])
            except Exception as e:
                gLog.info("select err {0}".format(e))
                break

            for r in rs:
                if r == self.socket:
                    client, addr = self.socket.accept()
                    if not client:
                        gLog.log_tick("Socket accept failed")
                        continue
                    else:
                        self.connect(client)
                else:
                    try:
                        data = r.recv(256)
                        if len(data) == 0:
                            self.disconnect(r)
                        else:
                            gLog.log_tick(u"从{0}收到{1}个数据".format(r.getpeername(), len(data)))
                            #gLog.log(data)
                            self.unpack(data)
                    except socket.error:
                        self.disconnect(r)
                        continue

    def unpack(self, data):
        dlen = len(data)
        try:
            substr = str(binascii.b2a_hex(data))[2:-1]
            gLog.log(re.sub(r"(?<=\w)(?=(?:\w\w)+$)", " ", substr))
            if dlen < 24:
                gLog.log(u"数据帧长度不够")
                return
            gLog.log(u"数据帧解包:")

            head, ccid, cmd, sz, volt = struct.unpack('>2s8s1sBH', data[:14])
            headstr = str(binascii.b2a_hex(head))[2:-1]
            if headstr.lower() != "a503":
                gLog.log(u"数据帧格式不对")
            else:
                gLog.log(u"帧头:{0}".format(headstr))
                gLog.log("CCID:{0}".format(str(binascii.b2a_hex(ccid))[2:-1]))
                gLog.log("cmd :{0}".format(str(binascii.b2a_hex(cmd))[2:-1]))
                gLog.log("len :{0}".format(sz))
                gLog.log("volt:{0}mV".format(volt*2))
                if sz > 2: sz -= 2
                if (dlen-14) != sz:
                    gLog.log(u"有数据帧丢包")
                    if sz > dlen - 14 or sz < 10: sz = dlen - 14
                #fmat = '>{0}s'.format(sz)
                data = data[14:sz+14]
                gLog.log(u"探头上报事件:")
                for i in range(sz//10):
                    d = data[i*10:(i+1)*10]
                    addr, status, chrome, y, m, d, h, mm, sec = struct.unpack(">BBHBBBBBB", d)
                    gLog.log(u"[地址:{0}, 事件:{1:{2}<4}, 浓度:{3:<6d}, 时间:{4:0>2}-{5:0>2}-{6:0>2} {7:0>2}:{8:0>2}:{9:0>2}]".
                             format(addr,self.get_event_str(status),chr(12288),chrome, y, m, d, h, mm, sec))
        except Exception as e:
            gLog.log(u"数据帧有异常字符")

    def get_event_str(self, ev):
        if ev in self.ev_str.keys():
            return self.ev_str[ev]
        else:
            return u"未定义"

    def connect(self, client):
        self.server.append(client)
        gLog.log_tick("{0} clients, Socket accept {1}".format(len(self.server) - 1, client.getpeername()))

    def disconnect(self, client):
        flag = False
        for i, r in enumerate(self.server):
            if r == client:
                flag = True
                #self.server.remove(r)
                del self.server[i]
                break
        if flag:
            gLog.log_tick(u"{0}关闭".format(client.getpeername()))
            client.close()

if __name__ == "__main__":
    #server_addr = ("171.221.206.3", 9004)
    tcpserver = TcpServer("10.70.0.38")
    tcpserver.select_run()
